﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using Ganosoft.Container;

namespace GanoESB.Business
{
    public class Server
    {
        [Bean]
        protected ILogService logService;
        [Bean]
        protected IServiceBus serviceBus;
        [Bean]
        protected IMessageBus messageBus;

        private ServiceHost<IServiceBus> serviceBusHost;
        private ServiceHost<IMessageBus> messageBusHost;

        private Bindings bindings;

        /// <summary>
        /// 服务总线URL
        /// </summary>
        public string ServiceBusUrl { get; set; }

        /// <summary>
        /// 消息总线URL
        /// </summary>
        public string MessageBusUrl { get; set; }

        public void Start(string[] args)
        {
            AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles = "true";

            logService.Info(this.GetType(), "Server starting...");

            string[] serviceUrls = ServiceBusUrl.Split(';');
            string[] messageUrls = MessageBusUrl.Split(';');

            bindings = new Bindings();

            serviceBusHost = new ServiceHost<IServiceBus>(serviceBus, serviceUrls, bindings);
            serviceBusHost.EnableMetadataExchange = true;
            serviceBusHost.Open();

            messageBusHost = new ServiceHost<IMessageBus>(messageBus, messageUrls, bindings);
            messageBusHost.EnableMetadataExchange = true;
            messageBusHost.Open();

            serviceBus.Start();

            logService.Info(this.GetType(), "Server started.");
        }

        public void Stop()
        {
            serviceBus.Stop();
            //messageBus.Stop();
        }

        private class ServiceHost<T> : ServiceHost
        {
            public ServiceHost(Type type, string[] urls, Bindings bindings)
                : base(type)
            {
                Init(urls, bindings);
            }

            public ServiceHost(object target, string[] urls, Bindings bindings)
                : base(target)
            {
                Init(urls, bindings);
            }

            private void Init(string[] urls, Bindings bindings)
            {
                for (int i = 0; i < urls.Length; i++)
                {
                    if (urls[i].StartsWith("net.tcp"))
                        AddServiceEndpoint(typeof(T), bindings.TcpBinding, new Uri(urls[i]));
                    else if (urls[i].StartsWith("net.pipe"))
                        AddServiceEndpoint(typeof(T), bindings.PipeBinding, urls[i]);
                    else if (urls[i].StartsWith("http"))
                        AddServiceEndpoint(typeof(T), bindings.HttpBinding, urls[i]);
                }

                ServiceBehaviorAttribute sba = Description.Behaviors.Find<ServiceBehaviorAttribute>();
                if (sba == null)
                {
                    sba = new ServiceBehaviorAttribute();
                    Description.Behaviors.Add(sba);
                }
                sba.ConcurrencyMode = ConcurrencyMode.Multiple;
                sba.InstanceContextMode = InstanceContextMode.Single;

                ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior();
                stb.MaxConcurrentCalls = 100;
                stb.MaxConcurrentInstances = 100;
                stb.MaxConcurrentSessions = 100;
                Description.Behaviors.Add(stb);
            }

            #region 简化元数据交换行为的启用和关闭
            public bool EnableMetadataExchange
            {
                set
                {
                    if (State == CommunicationState.Opened)
                    {
                        throw new InvalidOperationException("Host is already opened");
                    }

                    ServiceMetadataBehavior metadataBehavior;
                    metadataBehavior = Description.Behaviors.Find<ServiceMetadataBehavior>();
                    if (metadataBehavior == null)
                    {
                        metadataBehavior = new ServiceMetadataBehavior();
                        //metadataBehavior.HttpGetEnabled = true;
                        Description.Behaviors.Add(metadataBehavior);
                    }
                    if (value == true)
                    {
                        if (HasMexEndpoint == false)
                        {
                            AddAllMexEndPoints();
                        }
                    }
                }
                get
                {
                    ServiceMetadataBehavior metadataBehavior;
                    metadataBehavior = Description.Behaviors.Find<ServiceMetadataBehavior>();
                    if (metadataBehavior == null)
                    {
                        return false;
                    }
                    return metadataBehavior.HttpGetEnabled;
                }
            }

            public bool HasMexEndpoint
            {
                get
                {
                    Predicate<ServiceEndpoint> mexEndPoint = delegate(ServiceEndpoint endpoint)
                    {
                        return endpoint.Contract.ContractType == typeof(IMetadataExchange);
                    };
                    return Array.Exists(new List<ServiceEndpoint>(Description.Endpoints).ToArray(), mexEndPoint);
                }
            }

            private void AddAllMexEndPoints()
            {
                Debug.Assert(HasMexEndpoint == false);

                foreach (Uri baseAddress in BaseAddresses)
                {
                    BindingElement bindingElement = null;
                    switch (baseAddress.Scheme)
                    {
                        case "net.tcp":
                            bindingElement = new TcpTransportBindingElement();
                            ((TcpTransportBindingElement)bindingElement).PortSharingEnabled = true;
                            break;
                        case "http":
                            bindingElement = new HttpTransportBindingElement();
                            break;
                        case "https":
                            bindingElement = new HttpsTransportBindingElement();
                            break;
                        case "net.pipe":
                            bindingElement = new NamedPipeTransportBindingElement();
                            break;
                    }
                    if (bindingElement != null)
                    {
                        Binding binding = new CustomBinding(bindingElement);
                        AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");
                    }
                }
            }
            #endregion
        }
    }
}
